package base

import (
	"encoding/base64"
	"fmt"
	"gitee.com/ymofen/gobase"
	"gitee.com/ymofen/gobase/gocache"
	"gitee.com/ymofen/golang/factory"
	"os"
	"strings"
)

const (
	envFlag = iota
	varFlag
	cacheFlag
)

const (
	strFlag = iota
	objectFlag
)

const (
	codeNothing = iota
	codeUrlEncodeFlag
	codeJsonEncodeFlag
	codeBase64EncodeFlag
)

func PlgReplacePlaceholder(s string) string {
	start := "$$"
	end := "$$"
	idx := 0
	var replkey string

	for {
		id := gobase.GetBetween(s, start, end, idx)
		if len(id) == 0 {
			break
		}

		rawrepl := id

		// env.cpyidx % 10
		if ok, id := gobase.CheckTrimPrefix(id, "env."); ok { // 环境变量
			isObject, id := gobase.CheckTrimSuffix(id, ".object")
			strv := os.Getenv(id)
			if isObject {
				replkey = fmt.Sprintf("\"%s%s%s\"", start, rawrepl, end)
			} else {
				replkey = fmt.Sprintf("%s%s%s", start, rawrepl, end)
				strv = gobase.EscapeJsonStrSimple(strv)
			}
			s = strings.ReplaceAll(s, replkey, strv)
			continue
		}

		isObject, id := gobase.CheckTrimSuffix(id, ".object")

		if ok, v := InvokeFactoryVarFunc(id, nil); ok {
			strv := gobase.GetStrValue(v, "")
			if isObject {
				replkey = fmt.Sprintf("\"%s%s%s\"", start, rawrepl, end)
			} else {
				replkey = fmt.Sprintf("%s%s%s", start, rawrepl, end)
				strv = gobase.EscapeJsonStrSimple(strv)
			}
			s = strings.ReplaceAll(s, replkey, strv)
			continue
		}
		// 替换失败忽略
		idx++
	}

	return gobase.ReplacePlaceholder(s, "${", "}$", func(key string) (v string, ok bool) {
		return gobase.ParseSimpleExp(key)
	})
}

func ReplacePlaceholder(s string, start, end string, getKeyValue func(key, start, end string) (repl, v string, ok bool)) string {
	idx := 0
	for {
		id := gobase.GetBetween(s, start, end, idx)
		if len(id) == 0 {
			break
		}

		if repl, v, ok := getKeyValue(id, start, end); ok {
			// 注意 返回的值(v)如果继续包含replkey, 会造成死循环
			s = strings.ReplaceAll(s, repl, v)
		} else {
			idx++
		}
	}
	return s
}

func TryGetFactoryVarAsString(id string, sender interface{}, args ...interface{}) (v string, ok bool) {
	obj := gobase.GetFactoryVar(id)
	if obj != nil {
		if fn, ok := obj.(gobase.TFactoryVarFunc); ok {
			val := fn(sender, args...)
			return gobase.GetStrValue(val, ""), true
		} else {
			return gobase.GetStrValue(obj, ""), true
		}
	}
	return "", false
}

func TryGetFactoryAsString(id string, def string) string {
	v := gobase.GetFactoryVar(id)
	if v == nil {
		return def
	}
	if fn, ok := v.(gobase.TFactoryVarFunc); ok {
		v = fn(nil)
	}
	return gobase.GetStrValue(v, def)
}

func TryGetKeyValue4ReplacePlaceholder(key string, start, end string, finalGet func(key string) (v string, ok bool)) (repl, v string, ok bool) {

	// preflag.var.code.suffix
	var preflag int8 = varFlag
	var suffixflag int8 = strFlag
	var codeFlag int8 = codeNothing

	key0, ok0 := gobase.TryTrimPrefix(key, "env.")
	if ok0 {
		preflag = envFlag
	} else {
		key0, ok0 = gobase.TryTrimPrefix(key0, "cache.")
		if ok0 {
			preflag = cacheFlag
		}
	}

	key0, ok0 = gobase.TryTrimSuffix(key0, ".object")
	if ok0 {
		suffixflag = objectFlag
	}

	{ // convert
		key1, suffix1 := LastSplit2Str(key0, func(r rune) bool {
			return r == '.'
		})
		if suffix1 == "urlEncode" {
			codeFlag = codeUrlEncodeFlag
			key0 = key1
		} else if suffix1 == "jsonEncode" {
			codeFlag = codeJsonEncodeFlag
			key0 = key1
		} else if suffix1 == "base64Encode" {
			codeFlag = codeBase64EncodeFlag
			key0 = key1
		}
	}

	if preflag == varFlag {
		v, ok = TryGetFactoryVarAsString(key0, nil)
	} else if preflag == envFlag {
		v = os.Getenv(key0)
		ok = true
	} else {
		v1, ok1 := gocache.Get(key0)
		if ok1 {
			v = gobase.GetStrValue(v1, "")
			ok = true
		}
	}
	if finalGet != nil { // 最后获取
		if len(v) > 0 {
			v1, ok1 := finalGet(v)
			if ok1 {
				v, ok = v1, true
			}
		} else {
			v1, ok1 := finalGet(key0)
			if ok1 {
				v, ok = v1, true
			} else {
				ok = false // 没有获取到
			}
		}
	}

	if ok {
		if codeFlag == codeUrlEncodeFlag {
			v = gobase.UrlEncode0(v)
		} else if codeFlag == codeJsonEncodeFlag {
			v = gobase.EscapeJsonStrSimple(v)
		} else if codeFlag == codeBase64EncodeFlag {
			v = base64.StdEncoding.EncodeToString([]byte(v))
		}

		if suffixflag == objectFlag {
			repl = fmt.Sprintf("\"%s%s%s\"", start, key, end)
		} else {
			repl = fmt.Sprintf("%s%s%s", start, key, end)
		}
	}

	return
}

func LastIndexRunes(s []rune, f func(rune) bool) int {
	for i := len(s) - 1; i > 0; i-- {
		if f(s[i]) {
			return i
		}
	}
	return -1
}

func LastSplit2Str(s string, f func(rune) bool) (s0 string, s1 string) {
	if len(s) == 0 {
		return s, ""
	}
	runes := []rune(s)
	idx := LastIndexRunes(runes, f)
	if idx == -1 {
		return s, ""
	}
	return string(runes[:idx]), string(runes[idx+1:])
}

func VarToStr(v interface{}) string {
	s := gobase.GetStrValue(v, "")
	if len(s) == 0 {
		return fmt.Sprintf("%p", v)
	}
	return s
}

func InvokeFactoryVarFunc(id interface{}, sender interface{}, args ...interface{}) (bool, interface{}) {
	obj := factory.DefaultVarStorer.GetVar(VarToStr(id))
	if obj != nil {
		if fn, ok := obj.(func(sender interface{}, args ...interface{}) interface{}); ok {
			return true, fn(sender, args...)
		} else {
			return true, obj
		}
	}
	return false, nil
}
